package com.kexilo.core.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.support.CompositeCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Spring Cache配置
 * 多级缓存：Caffeine(L1) → Redis(L2)
 * 
 * @author Kexilo
 */
@Configuration
@EnableCaching
public class CacheConfig {
    
    private static final Logger log = LoggerFactory.getLogger(CacheConfig.class);

    /**
     * Redis缓存管理器（L2缓存）
     */
    @Bean
    @Primary
    public CacheManager redisCacheManager(RedisConnectionFactory factory) {
        // Redis序列化配置
        RedisCacheConfiguration config = RedisCacheConfiguration
            .defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30)) // 默认30分钟过期
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(createRedisSerializer()))
            .disableCachingNullValues(); // 不缓存null值
            
        // 针对不同业务设置不同的TTL
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        
        // 用户权限缓存 - 5分钟
        cacheConfigurations.put("user:permissions", config.entryTtl(Duration.ofMinutes(5)));
        
        // 用户角色缓存 - 15分钟
        cacheConfigurations.put("user:roles", config.entryTtl(Duration.ofMinutes(15)));
        
        // 菜单树缓存 - 30分钟
        cacheConfigurations.put("menu:tree", config.entryTtl(Duration.ofMinutes(30)));
        
        // 部门层级缓存 - 15分钟
        cacheConfigurations.put("dept:children", config.entryTtl(Duration.ofMinutes(15)));
        
        // 字典数据缓存 - 1小时
        cacheConfigurations.put("dict:data", config.entryTtl(Duration.ofHours(1)));
        
        // 系统配置缓存 - 24小时
        cacheConfigurations.put("config:data", config.entryTtl(Duration.ofHours(24)));
        
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .withInitialCacheConfigurations(cacheConfigurations)
            .transactionAware() // 支持事务
            .build();
    }

    /**
     * Caffeine本地缓存管理器（L1缓存）
     */
    @Bean("localCacheManager")
    public CacheManager localCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        
        // 配置Caffeine缓存
        cacheManager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(50000)           // 最大缓存条目数
            .expireAfterWrite(30, TimeUnit.MINUTES)  // 写入后30分钟过期
            .expireAfterAccess(10, TimeUnit.MINUTES) // 访问后10分钟过期
            .recordStats());              // 启用统计
            
        // 预定义缓存名称
        cacheManager.setCacheNames(Arrays.asList(
            "dict:data:local",    // 字典数据本地缓存
            "config:data:local",  // 配置数据本地缓存
            "menu:tree:local"     // 菜单树本地缓存
        ));
        
        return cacheManager;
    }

    /**
     * 多级缓存管理器（组合缓存管理器）
     */
    @Bean("compositeCacheManager")
    public CompositeCacheManager compositeCacheManager(
            @Qualifier("localCacheManager") CacheManager localCache,
            CacheManager redisCache) {
        
        CompositeCacheManager composite = new CompositeCacheManager();
        // 设置缓存管理器优先级：本地缓存 > Redis缓存
        composite.setCacheManagers(Arrays.asList(localCache, redisCache));
        composite.setFallbackToNoOpCache(false); // 不使用NoOp缓存
        
        log.info("多级缓存管理器初始化完成: Caffeine(L1) -> Redis(L2)");
        return composite;
    }

    /**
     * 创建Redis序列化器
     */
    private GenericJackson2JsonRedisSerializer createRedisSerializer() {
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        
        // 设置类型信息，用于反序列化
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
                                ObjectMapper.DefaultTyping.NON_FINAL, 
                                JsonTypeInfo.As.WRAPPER_ARRAY);
        
        return new GenericJackson2JsonRedisSerializer(om);
    }

    /**
     * 缓存键生成器（自定义）
     */
    @Bean("customKeyGenerator")
    public org.springframework.cache.interceptor.KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder key = new StringBuilder();
            key.append(target.getClass().getSimpleName()).append(":");
            key.append(method.getName()).append(":");
            
            if (params.length > 0) {
                for (Object param : params) {
                    if (param != null) {
                        key.append(param.toString()).append(":");
                    }
                }
                // 移除最后一个冒号
                key.setLength(key.length() - 1);
            }
            
            return key.toString();
        };
    }
}
